<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<title>寻找相关词(shingles) | Elasticsearch: 权威指南 | Elastic</title>
    <!-- Give IE8 a fighting chance -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
	<link rel="stylesheet" type="text/css" href="../static/styles.css" />
</head>
<body>
<div class="main-container">
    <section id="content">
        
        <div class="content-wrapper">
            <section id="guide" lang="zh_cn">
                <div class="container">
                    <div class="row">
                        <div class="col-xs-12 col-sm-8 col-md-8 guide-section">
                            <div style="color:gray; word-break: break-all; font-size:12px;">原文地址: <a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/shingles.html" rel="nofollow">https://www.elastic.co/guide/cn/elasticsearch/guide/current/shingles.html</a>, 版权归 www.elastic.co 所有<br/>
                            英文版地址: <a href="https://www.elastic.co/guide/en/elasticsearch/guide/current/shingles.html" rel="nofollow">https://www.elastic.co/guide/en/elasticsearch/guide/current/shingles.html</a>
                            </div>
                        <!-- start body -->
                  <div class="page_header">
<b>请注意:</b><br>本书基于 Elasticsearch 2.x 版本，有些内容可能已经过时。
</div>
<div id="content">
<div class="breadcrumbs">
<span class="breadcrumb-link"><a href="index.html">Elasticsearch: 权威指南</a></span>
»
<span class="breadcrumb-link"><a href="search-in-depth.html">深入搜索</a></span>
»
<span class="breadcrumb-link"><a href="proximity-matching.html">近似匹配</a></span>
»
<span class="breadcrumb-node">寻找相关词(shingles)</span>
</div>
<div class="navheader">
<span class="prev">
<a href="_Improving_Performance.html">« 性能优化</a>
</span>
<span class="next">
<a href="partial-matching.html">部分匹配 »</a>
</span>
</div>
<div class="section">
<div class="titlepage"><div><div>
<h2 class="title">
<a id="shingles"></a>寻找相关词(shingles)<a class="edit_me edit_me_private" rel="nofollow" title="Editing on GitHub is available to Elastic" href="https://github.com/elasticsearch-cn/elasticsearch-definitive-guide/edit/cn/120_Proximity_Matching/35_Shingles.asciidoc">edit</a>
</h2>
</div></div></div>
<p>短语查询和邻近查询都很好用，但仍有一个缺点。它们过于严格了：为了匹配短语查询，所有词项都必须存在，即使使用了 <code class="literal">slop</code> 。
</p>
<p>用 <code class="literal">slop</code> 得到的单词顺序的灵活性也需要付出代价，因为失去了单词对之间的联系。即使可以识别 <code class="literal">sue</code> 、 <code class="literal">alligator</code> 和 <code class="literal">ate</code>
相邻出现的文档，但无法分辨是 <em>Sue ate</em> 还是  <em>alligator ate</em> 。</p>
<p>当单词相互结合使用的时候，表达的含义比单独使用更丰富。两个子句 <em>I’m not happy I’m working</em> 和 <em>I’m happy I’m not working</em> 包含相同
的单词，也拥有相同的邻近度，但含义截然不同。</p>
<p>如果索引单词对而不是索引独立的单词，就能对这些单词的上下文尽可能多的保留。</p>
<p>对句子 <code class="literal">Sue ate the alligator</code> ，不仅要将每一个单词（或者 <em>unigram</em> ）作为词项索引</p>
<pre class="literallayout">["sue", "ate", "the", "alligator"]</pre>

<p>也要将每个单词 <em>以及它的邻近词</em> 作为单个词项索引：</p>
<pre class="literallayout">["sue ate", "ate the", "the alligator"]</pre>

<p>这些单词对（或者 <em>bigrams</em> ）被称为 <em>shingles</em> 。</p>
<div class="tip admon">
<div class="icon"></div>
<div class="admon_content">
<p>Shingles 不限于单词对；你也可以索引三个单词（ <em>trigrams</em> ）：</p>
<pre class="literallayout">["sue ate the", "ate the alligator"]</pre>

<p>Trigrams 提供了更高的精度，但是也大大增加了索引中唯一词项的数量。在大多数情况下，Bigrams 就够了。</p>
</div>
</div>
<p>当然，只有当用户输入的查询内容和在原始文档中顺序相同时，shingles 才是有用的；对 <code class="literal">sue alligator</code> 的查询可能会匹配到单个单词，但是不会匹配任何 shingles 。</p>
<p>幸运的是，用户倾向于使用和搜索数据相似的构造来表达搜索意图。但这一点很重要：只是索引 bigrams 是不够的；我们仍然需要 unigrams ，但可以将匹配 bigrams 作为增加相关度评分的信号。</p>
<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_生成_shingles"></a>生成 Shingles<a class="edit_me edit_me_private" rel="nofollow" title="Editing on GitHub is available to Elastic" href="https://github.com/elasticsearch-cn/elasticsearch-definitive-guide/edit/cn/120_Proximity_Matching/35_Shingles.asciidoc">edit</a>
</h3>
</div></div></div>
<p>Shingles 需要在索引时作为分析过程的一部分被创建。我们可以将 unigrams 和 bigrams 都索引到单个字段中，
但将它们分开保存在能被独立查询的字段会更清晰。unigrams 字段将构成我们搜索的基础部分，而 bigrams 字段用来提高相关度。</p>
<p>首先，我们需要在创建分析器时使用 <code class="literal">shingle</code> 语汇单元过滤器：</p>
<div class="pre_wrapper lang-sense">
<pre class="programlisting prettyprint lang-sense">DELETE /my_index

PUT /my_index
{
    "settings": {
        "number_of_shards": 1,  <a id="CO88-1"></a><i class="conum" data-value="1"></i>
        "analysis": {
            "filter": {
                "my_shingle_filter": {
                    "type":             "shingle",
                    "min_shingle_size": 2, <a id="CO88-2"></a><i class="conum" data-value="2"></i>
                    "max_shingle_size": 2, <a id="CO88-3"></a><i class="conum" data-value="2"></i>
                    "output_unigrams":  false   <a id="CO88-4"></a><i class="conum" data-value="3"></i>
                }
            },
            "analyzer": {
                "my_shingle_analyzer": {
                    "type":             "custom",
                    "tokenizer":        "standard",
                    "filter": [
                        "lowercase",
                        "my_shingle_filter" <a id="CO88-5"></a><i class="conum" data-value="4"></i>
                    ]
                }
            }
        }
    }
}</pre>
</div>
<div class="sense_widget" data-snippet="snippets/120_Proximity_Matching/35_Shingles.json"></div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO88-1"><i class="conum" data-value="1"></i></a></p>
</td>
<td align="left" valign="top">
<p>参考 <a class="xref" href="relevance-is-broken.html" title="被破坏的相关度！">被破坏的相关度！</a> 。</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO88-2"><i class="conum" data-value="2"></i></a><a href="#CO88-3"></a></p>
</td>
<td align="left" valign="top">
<p>默认最小/最大的 shingle 大小是 <code class="literal">2</code> ，所以实际上不需要设置。</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO88-4"><i class="conum" data-value="3"></i></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">shingle</code> 语汇单元过滤器默认输出 unigrams ，但是我们想让 unigrams 和 bigrams 分开。</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO88-5"><i class="conum" data-value="4"></i></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">my_shingle_analyzer</code> 使用我们常规的 <code class="literal">my_shingles_filter</code> 语汇单元过滤器。</p>
</td>
</tr>
</table>
</div>
<p>首先，用 <code class="literal">analyze</code> API 测试下分析器：</p>
<div class="pre_wrapper lang-js">
<pre class="programlisting prettyprint lang-js">GET /my_index/_analyze?analyzer=my_shingle_analyzer
Sue ate the alligator</pre>
</div>
<p>果然， 我们得到了 3 个词项：</p>
<div class="ulist itemizedlist">
<ul class="itemizedlist">
<li class="listitem">
<code class="literal">sue ate</code>
</li>
<li class="listitem">
<code class="literal">ate the</code>
</li>
<li class="listitem">
<code class="literal">the alligator</code>
</li>
</ul>
</div>
<p>现在我们可以继续创建一个使用新的分析器的字段。</p>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_多字段"></a>多字段<a class="edit_me edit_me_private" rel="nofollow" title="Editing on GitHub is available to Elastic" href="https://github.com/elasticsearch-cn/elasticsearch-definitive-guide/edit/cn/120_Proximity_Matching/35_Shingles.asciidoc">edit</a>
</h3>
</div></div></div>
<p>我们曾谈到将 unigrams 和 bigrams 分开索引更清晰，所以 <code class="literal">title</code> 字段将创建成一个多字段（参考 <a class="xref" href="multi-fields.html" title="字符串排序与多字段">字符串排序与多字段</a> ）：</p>
<div class="pre_wrapper lang-js">
<pre class="programlisting prettyprint lang-js">PUT /my_index/_mapping/my_type
{
    "my_type": {
        "properties": {
            "title": {
                "type": "string",
                "fields": {
                    "shingles": {
                        "type":     "string",
                        "analyzer": "my_shingle_analyzer"
                    }
                }
            }
        }
    }
}</pre>
</div>
<p>通过这个映射， JSON 文档中的 <code class="literal">title</code> 字段将会被以 unigrams (<code class="literal">title</code>)和 bigrams (<code class="literal">title.shingles</code>)被索引，这意味着可以独立地查询这些字段。</p>
<p>最后，我们可以索引以下示例文档:</p>
<div class="pre_wrapper lang-js">
<pre class="programlisting prettyprint lang-js">POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "Sue ate the alligator" }
{ "index": { "_id": 2 }}
{ "title": "The alligator ate Sue" }
{ "index": { "_id": 3 }}
{ "title": "Sue never goes anywhere without her alligator skin purse" }</pre>
</div>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_搜索_shingles"></a>搜索 Shingles<a class="edit_me edit_me_private" rel="nofollow" title="Editing on GitHub is available to Elastic" href="https://github.com/elasticsearch-cn/elasticsearch-definitive-guide/edit/cn/120_Proximity_Matching/35_Shingles.asciidoc">edit</a>
</h3>
</div></div></div>
<p>为了理解添加 <code class="literal">shingles</code> 字段的好处，让我们首先来看 <code class="literal">The hungry alligator ate Sue</code> 进行简单 <code class="literal">match</code> 查询的结果：</p>
<div class="pre_wrapper lang-js">
<pre class="programlisting prettyprint lang-js">GET /my_index/my_type/_search
{
   "query": {
        "match": {
           "title": "the hungry alligator ate sue"
        }
   }
}</pre>
</div>
<p>这个查询返回了所有的三个文档， 但是注意文档 1 和 2 有相同的相关度评分因为他们包含了相同的单词：</p>
<div class="pre_wrapper lang-js">
<pre class="programlisting prettyprint lang-js">{
  "hits": [
     {
        "_id": "1",
        "_score": 0.44273707, <a id="CO89-1"></a><i class="conum" data-value="1"></i>
        "_source": {
           "title": "Sue ate the alligator"
        }
     },
     {
        "_id": "2",
        "_score": 0.44273707, <a id="CO89-2"></a><i class="conum" data-value="1"></i>
        "_source": {
           "title": "The alligator ate Sue"
        }
     },
     {
        "_id": "3", <a id="CO89-3"></a><i class="conum" data-value="2"></i>
        "_score": 0.046571054,
        "_source": {
           "title": "Sue never goes anywhere without her alligator skin purse"
        }
     }
  ]
}</pre>
</div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO89-1"><i class="conum" data-value="1"></i></a><a href="#CO89-2"></a></p>
</td>
<td align="left" valign="top">
<p>两个文档都包含 <code class="literal">the</code> 、 <code class="literal">alligator</code> 和 <code class="literal">ate</code> ，所以获得相同的评分。</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO89-3"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p>我们可以通过设置 <code class="literal">minimum_should_match</code> 参数排除文档 3 ，参考 <a class="xref" href="match-multi-word.html#match-precision" title="控制精度">控制精度</a> 。</p>
</td>
</tr>
</table>
</div>
<p>现在在查询里添加 <code class="literal">shingles</code> 字段。不要忘了在 <code class="literal">shingles</code> 字段上的匹配是充当一
种信号—​为了提高相关度评分—​所以我们仍然需要将基本 <code class="literal">title</code> 字段包含到查询中：</p>
<div class="pre_wrapper lang-js">
<pre class="programlisting prettyprint lang-js">GET /my_index/my_type/_search
{
   "query": {
      "bool": {
         "must": {
            "match": {
               "title": "the hungry alligator ate sue"
            }
         },
         "should": {
            "match": {
               "title.shingles": "the hungry alligator ate sue"
            }
         }
      }
   }
}</pre>
</div>
<p>仍然匹配到了所有的 3 个文档， 但是文档 2 现在排到了第一名因为它匹配了 shingled 词项 <code class="literal">ate sue</code>.</p>
<div class="pre_wrapper lang-js">
<pre class="programlisting prettyprint lang-js">{
  "hits": [
     {
        "_id": "2",
        "_score": 0.4883322,
        "_source": {
           "title": "The alligator ate Sue"
        }
     },
     {
        "_id": "1",
        "_score": 0.13422975,
        "_source": {
           "title": "Sue ate the alligator"
        }
     },
     {
        "_id": "3",
        "_score": 0.014119488,
        "_source": {
           "title": "Sue never goes anywhere without her alligator skin purse"
        }
     }
  ]
}</pre>
</div>
<p>即使查询包含的单词 <code class="literal">hungry</code> 没有在任何文档中出现，我们仍然使用单词邻近度返回了最相关的文档。</p>
</div>

<div class="section">
<div class="titlepage"><div><div>
<h3 class="title">
<a id="_performance性能"></a>Performance性能<a class="edit_me edit_me_private" rel="nofollow" title="Editing on GitHub is available to Elastic" href="https://github.com/elasticsearch-cn/elasticsearch-definitive-guide/edit/cn/120_Proximity_Matching/35_Shingles.asciidoc">edit</a>
</h3>
</div></div></div>
<p>shingles 不仅比短语查询更灵活，而且性能也更好。
shingles 查询跟一个简单的 <code class="literal">match</code> 查询一样高效，而不用每次搜索花费短语查询的代价。只是在索引期间因为更多词项需要被索引会付出一些小的代价，
这也意味着有 shingles 的字段会占用更多的磁盘空间。
然而，大多数应用写入一次而读取多次，所以在索引期间优化我们的查询速度是有意义的。</p>
<p>这是一个在 Elasticsearch 里会经常碰到的话题：不需要任何前期进行过多的设置，就能够在搜索的时候有很好的效果。
一旦更清晰的理解了自己的需求，就能在索引时通过正确的为你的数据建模获得更好结果和性能。
</p>
</div>

</div>
<div class="navfooter">
<span class="prev">
<a href="_Improving_Performance.html">« 性能优化</a>
</span>
<span class="next">
<a href="partial-matching.html">部分匹配 »</a>
</span>
</div>
</div>

                  <!-- end body -->
                        </div>
                        <div class="col-xs-12 col-sm-4 col-md-4" id="right_col">
                        
                        </div>
                    </div>
                </div>
            </section>
        </div>
    </section>
</div>
<script src="../static/cn.js"></script>
</body>
</html>